package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.common.cache.GmallCache;
import com.atguigu.gmall.common.config.RedissonConfig;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.product.*;
import com.atguigu.gmall.product.mapper.*;
import com.atguigu.gmall.product.service.ProductService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private BaseCategoryViewMapper baseCategoryViewMapper;

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuImageMapper skuImageMapper;

    @Autowired
    private SpuSaleAttrMapper spuSaleAttrMapper;

    @Autowired
    private SpuSaleAttrValueMapper spuSaleAttrValueMapper;

    @Autowired
    private SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    
    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private SkuAttrValueMapper skuAttrValueMapper;

    @Override
    @GmallCache(prefix = "getBaseCategoryView")
    public BaseCategoryView getBaseCategoryView(Long category3Id) {
        return baseCategoryViewMapper.selectById(category3Id);
    }

    @Override
    @GmallCache(prefix = "getSkuInfo")
    public SkuInfo getSkuInfo(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if(skuInfo != null) {
            List<SkuImage> skuImageList = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
            skuInfo.setSkuImageList(skuImageList);
        }
        return skuInfo;
    }

    public SkuInfo getSkuInfo1(Long skuId) {
        // 利用Redisson工具实现锁，底层还是Redis锁跟下边一样，只是封装了一下
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if(skuInfo == null){
            RLock lock = redissonClient.getLock(lockKey);
            try {
                boolean tryLock = lock.tryLock(1, 1, TimeUnit.SECONDS);
                if(tryLock){
                    try {
                        skuInfo = skuInfoMapper.selectById(skuId);
                        if(skuInfo != null){
                            List<SkuImage> skuImageList = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                            skuInfo.setSkuImageList(skuImageList);
                            redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(3000), TimeUnit.SECONDS);
                        }else{
                            skuInfo = new SkuInfo();
                            redisTemplate.opsForValue().set(cacheKey,skuInfo,5, TimeUnit.MINUTES);
                        }
                    } finally {
                        lock.unlock();
                    }
                }else{
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return skuInfo;

        /* 利用Redis锁实现
        String cacheKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;
        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;
        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
        if(skuInfo == null){
            String uuid = UUID.randomUUID().toString();
            Boolean isFlag = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, 1, TimeUnit.SECONDS);
            if(isFlag){
                skuInfo = skuInfoMapper.selectById(skuId);
                if(skuInfo != null){
                    List<SkuImage> skuImageList = skuImageMapper.selectList(new QueryWrapper<SkuImage>().eq("sku_id", skuId));
                    skuInfo.setSkuImageList(skuImageList);
                    redisTemplate.opsForValue().set(cacheKey,skuInfo,RedisConst.SKUKEY_TIMEOUT + new Random().nextInt(3000), TimeUnit.SECONDS);
                }else{
                    skuInfo = new SkuInfo();
                    redisTemplate.opsForValue().set(cacheKey,skuInfo,5, TimeUnit.MINUTES);
                }
                String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return tostring(redis.call('del',KEYS[1])) else return 0 end";
                this.redisTemplate.execute(new DefaultRedisScript<>(script), Collections.singletonList(lockKey), uuid);
            }else{
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return (SkuInfo) redisTemplate.opsForValue().get(cacheKey);
            }
        }
        return skuInfo;
        */
    }

    @Override
    public BigDecimal getPrice(Long skuId) {
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        if (null != skuInfo) {
            return skuInfo.getPrice();
        }
        return null;
    }

    @Override
    @GmallCache(prefix = "getSpuSaleAttrAndChecked")
    public List<SpuSaleAttr> getSpuSaleAttrAndChecked(Long spuId, Long skuId) {
        //采用三表联查方法
        return spuSaleAttrMapper.getSpuSaleAttrAndChecked(spuId, skuId);

        //单表查询 利用两个ID单独查询两张表，将结果由java代码进行处理
        /*List<SpuSaleAttrValue> list = spuSaleAttrValueMapper.selectList(new QueryWrapper<SpuSaleAttrValue>().eq("spu_id", spuId));
        List<SkuSaleAttrValue> list1 = skuSaleAttrValueMapper.selectList(new QueryWrapper<SkuSaleAttrValue>().eq("sku_Id", skuId));
        List<SpuSaleAttr> spuSaleAttrList = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            boolean flag = true;
            list.get(i).setIsChecked("0");
            for (SpuSaleAttr saleAttr : spuSaleAttrList) {
                if (list.get(i).getBaseSaleAttrId() == saleAttr.getBaseSaleAttrId()) {
                    for (SkuSaleAttrValue skuSaleAttrValue : list1) {
                        if(skuSaleAttrValue.getSaleAttrValueId() == list.get(i).getId()){
                            list.get(i).setIsChecked("1");
                        }
                    }
                    saleAttr.getSpuSaleAttrValueList().add(list.get(i));
                    flag = false;
                }
            }
            if (flag) {
                SpuSaleAttr spuSaleAttr = new SpuSaleAttr();
                spuSaleAttr.setBaseSaleAttrId(list.get(i).getBaseSaleAttrId());
                spuSaleAttr.setSaleAttrName(list.get(i).getSaleAttrName());
                spuSaleAttr.setSpuSaleAttrValueList(new ArrayList<>());
                for (SkuSaleAttrValue skuSaleAttrValue : list1) {
                    if(skuSaleAttrValue.getSaleAttrValueId() == list.get(i).getId()){
                        list.get(i).setIsChecked("1");
                    }
                }
                spuSaleAttr.getSpuSaleAttrValueList().add(list.get(i));
                spuSaleAttrList.add(spuSaleAttr);
            }
        }
        return spuSaleAttrList;*/
    }

    @Override
    @GmallCache(prefix = "getSaleAttrValuesBySpu")
    public Map getSaleAttrValuesBySpu(Long spuId) {
        Map result = new HashMap();
        List<Map> listMap = skuSaleAttrValueMapper.getSaleAttrValuesBySpu(spuId);
        for (Map map : listMap) {
            result.put(map.get("value_id"),map.get("sku_id"));
        }
        return result;
    }

    @Override
    public List<BaseCategoryView> getBaseCategoryViewList() {
        return baseCategoryViewMapper.selectList(null );
    }

    @Override
    public List<SkuAttrValue> getBaseAttrInfoAndBaseAttrValue(Long skuId) {
        return skuAttrValueMapper.getBaseAttrInfoAndBaseAttrValue(skuId);
    }

}
